// * 消息订阅与发布
// PubSub: 包含所有功能的订阅/发布消息的管理者
// PubSub.subscribe(msg, subscriber): 订阅消息: 指定消息名和订阅者回调函数
// PubSub.publish(msg, data): 异步发布消息: 指定消息名和数据
// PubSub.publishSync(msg, data): 同步发布消息: 指定消息名和数据
// PubSub.unsubscribe(flag): 取消订阅: 根据标识取消某个或某些消息的订阅


export const PubSub = {
  // 订阅唯一id
  id: 1,
  // 频道与回调保存容器
  callbacks: {
    // pay: {
    //   token_1: fn,
    //   token_2: fn
    // },
    // cancel: {
    //   token_3: fn,
    // }
  }
}

/**
 * * 订阅
 * @param {*} channel 订阅频道
 * @param {*} callback 回调函数
 * @returns token - 订阅编号
 */
PubSub.subscribe = function(channel, callback) {
  // 创建唯一的编号
  let token = 'token_' + this.id++
  // 判断 callbacks 属性中是否存在 channel
  if (this.callbacks[channel]) {
    this.callbacks[channel][token] = callback
  } else {
    this.callbacks[channel] = {
      [token]: callback
    }
  }
  // 返回订阅频道的id
  return token
}

/**
 * * 发布
 * @param {*} channel 
 * @param {*} data 
 */
PubSub.publish = function(channel, data) {
  // 获取保存的频道以及回调
  const callbacks = this.callbacks[channel]
  // 如果存在
  if (callbacks) {
    // 获取对象的所有值并且循环调用回调
    Object.values(callbacks).forEach(callback => {
      callback(data)
    })
  }
}

/*
4. 取消消息订阅
  1). 没有传值, flag为undefined
  2). 传入token字符串
  3). msgName字符串
*/
PubSub.unsubscribe = function(flag) {
  // 如果 flag 为 undefined，则清空所有订阅
  if (flag === undefined) {
    this.callbacks = {}
  } else if (typeof flag === 'string') {
    // 如果为 string 类型，先判断是否为 token_ 开头
    if (flag.indexOf('token_') === 0) {
      // 如果是，说明是一个订阅id，就获取对应id的回调对象
      let callbackObj = Object.values(this.callbacks).find(obj => obj.hasOwnProperty(flag))
      // 如果存在，删除对应属性
      if (callbackObj) {
        delete callbackObj[flag];
      }
    } else {
      // 说明是一个频道的名称
      delete this.callbacks[flag]
    }
  } else {
    throw new Error('如果传入参数，必须是字符串类型')
  }
}